import { frameYieldMs } from "../SchedulerFeatureFlags";
import { push, peek, pop } from "../SchedulerMinHeap";
import {
  ImmediatePriority,
  UserBlockingPriority,
  NormalPriority,
  LowPriority,
  IdlePriority,
} from "../SchedulerPriorities";

// 不同的任务优先级对应不同的超时时间
let maxSigned31BitInt = 1073741823;
// 立即
let IMMEDIATE_PRIORITY_TIMEOUT = -1;
// 用户阻塞的
let USER_BLOCKING_PRIORITY_TIMEOUT = 250;
let NORMAL_PRIORITY_TIMEOUT = 5000;
let LOW_PRIORITY_TIMEOUT = 10000;
// Never times out
let IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;

let frameInterval = frameYieldMs;

// 任务的最小堆数组
let taskQueue = [];
let timerQueue = [];
// 自增的id
let taskIdCounter = 1;
let isPerformingWork = false;
let isHostCallbackScheduled = false;
let isHostTimeoutScheduled = false;
// 当前调度的任务
let currentTask = null;
// 当前任务的优先级
let currentPriorityLevel = NormalPriority;
// 回调函数
let scheduledHostCallback = null;
let startTime = -1;

// https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/now
let getCurrentTime = () => performance.now();
const localSetImmediate =
  typeof setImmediate !== "undefined" ? setImmediate : null;
const localSetTimeout = typeof setTimeout === "function" ? setTimeout : null;

// 执行的就是这个函数
const performWorkUntilDeadline = () => {
  if (scheduledHostCallback !== null) {
    // 先获取任务执行的时间 相对的时间
    const currentTime = getCurrentTime();
    startTime = currentTime;
    const hasTimeRemaining = true;
    let hasMoreWork = true;
    try {
      // 执行缓存的callback
      hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
    } finally {
      if (hasMoreWork) {
        // 如果还有工作 继续执行
        schedulePerformWorkUntilDeadline();
      } else {
        scheduledHostCallback = null;
      }
    }
  }
};

let schedulePerformWorkUntilDeadline;
if (typeof localSetImmediate === "function") {
  // nodejs and old IE
  schedulePerformWorkUntilDeadline = () => {
    localSetImmediate(performWorkUntilDeadline);
  };
} else if (typeof MessageChannel !== "undefined") {
  const channel = new MessageChannel();
  const port = channel.port2;
  channel.port1.onmessage = performWorkUntilDeadline;

  schedulePerformWorkUntilDeadline = () => {
    port.postMessage(null);
  };
} else {
  schedulePerformWorkUntilDeadline = () => {
    // setTimeout
    localSetTimeout(performWorkUntilDeadline, 0);
  };
}

/**
 * 按优先级 调度任务
 * @param {*} priorityLevel 优先级
 * @param {*} callback 回调任务
 * @param {*} options
 */
function unstable_scheduleCallback(priorityLevel, callback, options) {
  // console.log("unstable_scheduleCallback");
  // requestIdleCallback(callback);
  // 有时间有工作接着执行

  let currentTime = getCurrentTime();
  // 1. 任务的开始时间
  let startTime;
  // 会存在一些延时的任务
  if (typeof options === "object" && options !== null) {
    let delay = options.delay;
    if (typeof delay === "number" && delay > 0) {
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }
  } else {
    startTime = currentTime;
  }
  // 2. 超时时间 不同的优先级时间是不一样的
  let timeout;
  switch (priorityLevel) {
    case ImmediatePriority:
      timeout = IMMEDIATE_PRIORITY_TIMEOUT;
      break;
    case UserBlockingPriority:
      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;
      break;
    case IdlePriority:
      timeout = IDLE_PRIORITY_TIMEOUT;
      break;
    case LowPriority:
      timeout = LOW_PRIORITY_TIMEOUT;
      break;
    case NormalPriority:
    default:
      timeout = NORMAL_PRIORITY_TIMEOUT;
      break;
  }

  // 3. 过期时间  作为最小堆的排序字段 sortIndex
  let expirationTime = startTime + timeout;
  // 任务
  const newTask = {
    id: taskIdCounter++, // 自增的id
    callback, // 回调函数 任务函数
    priorityLevel, // 优先级
    startTime, // 开始时间
    expirationTime, // 过期时间
    sortIndex: -1, // 排序依据
  };
  if (startTime > currentTime) {
    // 延时的任务
  } else {
    // 过期时间作为排序的依据
    newTask.sortIndex = expirationTime;
    // 入队
    push(taskQueue, newTask);
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      // 调度任务
      requestHostCallback(flushWork);
    }
  }
  return newTask;
}

function requestHostCallback(callback) {
  scheduledHostCallback = callback;
  // 执行工作到截止时间
  schedulePerformWorkUntilDeadline();
}

/**
 * 开始执行任务队列中的任务
 * 从任务队列中不断的取出最高优先任务执行
 * 和workLoop是没关系的
 * @param {*} hasTimeRemaining
 * @param {*} initialTime
 */
function flushWork(hasTimeRemaining, initialTime) {
  isHostCallbackScheduled = false;
  isPerformingWork = true;
  // const previousPriorityLevel = currentPriorityLevel;
  try {
    return workLoop(hasTimeRemaining, initialTime);
  } finally {
    isPerformingWork = false;
  }
}

function workLoop(hasTimeRemaining, initialTime) {
  // console.log("workLoop", hasTimeRemaining, initialTime);
  let currentTime = initialTime;
  // 取出堆顶元素进行执行
  currentTask = peek(taskQueue);
  while (currentTask !== null) {
    // 没有过期但是时间不够了 需要放弃执行
    if (
      currentTask.expirationTime > currentTime &&
      (!hasTimeRemaining || shouldYieldToHost())
    ) {
      break;
    }
    // 过期的任务是不会暂停的 会立即执行完
    // 取出函数执行 这就是 reconciler中的 performConcurrentWorkOnRoot
    const callback = currentTask.callback;
    if (typeof callback === "function") {
      currentTask.callback = null;
      // currentPriorityLevel = currentTask.priorityLevel;
      const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
      // 可能返回一个继续执行的函数 fiber树的构建是比较耗时的
      const continuationCallback = callback(didUserCallbackTimeout);
      currentTime = getCurrentTime();
      if (typeof continuationCallback === "function") {
        // 返回了一个新的函数 表示还有任务要执行
        currentTask.callback = continuationCallback;
        return true;
      } else {
        // 任务完成了就弹出这个任务
        if (currentTask === peek(taskQueue)) {
          pop(taskQueue);
        }
      }
    } else {
      // 不是函数是没有意义的 这也是用来取消任务的
      pop(taskQueue);
    }
    // 还有任务要执行
    if (currentTask !== null) {
      return true;
    } else {
      // const firstTimer = peek(timerQueue);
      // if (firstTimer !== null) {
      //   requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
      // }
      return false;
    }
  }
}

function shouldYieldToHost() {
  const timeElapsed = getCurrentTime() - startTime;
  // react每帧申请5ms的时间片
  if (timeElapsed < frameInterval) return false;
  return true;
}

// 取消任务
function unstable_cancelCallback(task) {
  console.log("unstable_cancelCallback");
  task.callback = null;
}

export {
  getCurrentTime as unstable_now,
  ImmediatePriority as unstable_ImmediatePriority,
  UserBlockingPriority as unstable_UserBlockingPriority,
  NormalPriority as unstable_NormalPriority,
  IdlePriority as unstable_IdlePriority,
  LowPriority as unstable_LowPriority,
  unstable_scheduleCallback,
  shouldYieldToHost as unstable_shouldYield,
  unstable_cancelCallback,
};
